using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Serialization;
using System.Web.Hosting;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Drawing;

using ASPNETLibrary.WebParts.Resources;
using ASPNETLibrary.WebParts.Configuration;
using System.IO;

namespace ASPNETLibrary.WebParts
{
    /// <summary>
    /// This is a base class for skinned web parts.
    /// Its purpose is to allow separation of View from Controller and Model by allowing
    /// UI designers/developers to design the UI in separate ASCX controls and application
    /// developers to implement Controller and Model in class that will inherit this 
    /// abstract class. 
    /// Another important feature of this class and a reason for the class to be
    /// used as base class for Web Parts is skinning possibility (meaning that there can be
    /// multiple skins (UI) for the same web part).
    /// </summary>
    [XmlRoot(Namespace = "ASPNETLibrary.WebParts")]
    public abstract class BaseSkinnedWebPart : WebPart
    {
        #region [Member variables]

        /// <summary>
        /// Name of theme in which skin for this web part is located.
        /// It will be set to "default" by default
        /// </summary>
        private string _themeName = "Default";

        /// <summary>
        /// Name of skin (ASCX control) for this web part
        /// </summary>
        private string _skinName;

        /// <summary>
        /// List holding references to required controls from the skin
        /// </summary>
        private List<SkinnedPartControlsValidator.RequiredControl> _requiredControls;

        #endregion [Member variables]

        #region [Constructor]
        protected BaseSkinnedWebPart()
        {
            //Set skin name to name of the web part; this name
            //will be used until user sets another name through the tool part
            this._skinName = base.GetType().Name;

            //Initialize list of required controls
            this._requiredControls = new List<SkinnedPartControlsValidator.RequiredControl>();
        }
        #endregion [Constructor]

        #region [CreateChildControls]
        protected sealed override void CreateChildControls()
        {
            this.Controls.Clear();
            base.CreateChildControls();

            string skinPath = this.SkinPath;
            string fullFilePath = HttpContext.Current.Server.MapPath(skinPath);
            if (!String.IsNullOrEmpty(skinPath) && File.Exists(fullFilePath))
            {
                Control skin = this.Page.LoadControl(this.SkinPath);
                if (skin != null)
                {
                    //Add skin (ASCX control) to Controls collection
                    this.Controls.Add(skin);

                    //Call AttachBaseSetOfChildControls and then AttachChildControls
                    //methods should do the same thing but the first one could be used in
                    //generated code and the second one could be used in manually written code
                    this.AttachBaseSetOfChildControls(skin);

                    //Call AttachChildControls method so that inherited class can
                    //attach all ASP.NET controls from skin to member variables that represent
                    //those controls
                    this.AttachChildControls(skin);

                    //Call AddBaseSetOfRequiredControls and then AddRequiredControls; both
                    //methods should do the same thing but the first one could be used in
                    //generated code and the second one could be used in manually written code
                    this.AddBaseSetOfRequiredControls();

                    //Call AddRequiredControls method to add all required ASP.NET controls
                    //from skin to internal list holding required controls
                    this.AddRequiredControls();

                    if (this.ValidateRequiredControls())
                    {
                        //Call Initialize method so that inherited class can perform
                        //additional initialization (if needed)
                        this.Initialize(skin);
                    }
                }
            }
            else
            {
                //If skin is not found show an error message
                string errorMessage = BaseSkinnedWebPartResources.CouldntFindSkin + this.SkinPath;
                this.ShowErrorMessage(errorMessage);
            }
        }
        #endregion [CreateChildControls]

        #region [AttachChildControls]

        /// <summary>
        /// AttachBaseSetOfChildControls and AttachChildControls
        /// methods should do the same thing but the first one could be used in
        /// generated code and the second one could be used in manually written code
        /// </summary>
        /// <param name="skin">A skin (ASCX control) associated with this web part</param>
        protected abstract void AttachBaseSetOfChildControls(Control skin);

        /// <summary>
        /// This method will be used by inherited classes to attach
        /// ASP.NET controls from skin to memeber variables representing those
        /// controls. 
        /// It doesn't need to be overriden and implemented in base class.
        /// </summary>
        /// <param name="skin">A skin (ASCX control) associated with this web part</param>
        protected virtual void AttachChildControls(Control skin)
        {
        }

        #endregion [AttachChildControls]

        #region [AddRequiredControls]

        /// <summary>
        /// AddBaseSetOfRequiredControls and AddRequiredControls
        /// methods should do the same thing but the first one could be used in
        /// generated code and the second one could be used in manually written code
        /// </summary>
        protected abstract void AddBaseSetOfRequiredControls();

        /// <summary>
        /// This method will be used by inherited classes to add all required ASP.NET controls
        /// from skin to internal list holding required controls. That list will be used
        /// later for validation of all required controls.
        /// It doesn't need to be overriden and implemented in base class.
        /// </summary>
        protected virtual void AddRequiredControls()
        {
        }

        #endregion [AddRequiredControls]

        #region [AddRequiredControl]
        /// <summary>
        /// Adds a <paramref name="control"/> to internal list of required controls.
        /// </summary>
        /// <param name="control">A control to be added to internal list of required controls</param>
        /// <param name="controlID">ID of control to be added to list of required controls</param>
        protected void AddRequiredControl(Control control, string controlID)
        {
            SkinnedPartControlsValidator.RequiredControl requiredControl =
                new SkinnedPartControlsValidator.RequiredControl(control, controlID);

            this._requiredControls.Add(requiredControl);
        }
        #endregion [AddRequiredControl]

        #region [ValidateRequiredControls]

        /// <summary>
        /// Checks if all required controls are present in skin and if not then
        /// shows an error message.
        /// </summary>
        /// <returns>Returns true if all required controls are present in skin and false
        /// otherwise.</returns>
        private bool ValidateRequiredControls()
        {
            bool controlsValid = false;

            string errorMessage;
            controlsValid = SkinnedPartControlsValidator.ControlsValid(this._requiredControls, out errorMessage);
            if (!controlsValid)
            {
                this.ShowErrorMessage(errorMessage);
            }
            return controlsValid;
        }

        #endregion [ValidateRequiredControls]

        #region [Initialize]

        /// <summary>
        /// An abstract method that will be used by classes inheriting this base class
        /// to perform additional Web Part initialization (if needed)
        /// </summary>
        /// <param name="skin">A skin (ASCX control) associated with this web part</param>
        protected abstract void Initialize(Control skin);

        #endregion [Initialize]

        #region [ShowErrorMessage]
        /// <summary>
        /// Creates a Label control with <paramref name="message"/> set to its Text property, 
        /// removes all existing controls from the Controls collection and
        /// adds the Label instance to Controls collection so that error message gets displayed
        /// on web page.
        /// </summary>
        /// <param name="message"></param>
        protected void ShowErrorMessage(string message)
        {
            this.Controls.Clear();

            Label errorMessage = new Label();
            errorMessage.Text = message;
            errorMessage.ForeColor = Color.Red;
            errorMessage.Font.Bold = true;

            this.Controls.Add(errorMessage);
        }
        #endregion [ShowErrorMessage]

        #region [Properties]
        /// <summary>
        /// Name of theme in which skin for this web part is located
        /// </summary>
        [Personalizable(PersonalizationScope.Shared), WebBrowsable(false)]
        public virtual string ThemeName
        {
            get
            {
                return this._themeName;
            }
            set
            {
                this._themeName = value;
            }
        }

        /// <summary>
        /// Name of skin (ASCX control) for this web part
        /// </summary>
        [Personalizable(PersonalizationScope.Shared), WebBrowsable(false)]
        public virtual string SkinName
        {
            get
            {
                return this._skinName;
            }
            set
            {
                this._skinName = value;
            }
        }
        
        /// <summary>
        /// Returns a path to skin for this web part
        /// </summary>
        protected string SkinPath
        {
            get
            {
                string retVal = null;

                WebPartSkin skin = ConfigHandler.Instance.GetSkin(this.ThemeName, this.SkinName);
                if (skin != null)
                {
                    retVal = skin.Path;
                }
                return retVal;
            }
        }

        #endregion [Properties]

        public override EditorPartCollection CreateEditorParts()
        {
            List<EditorPart> customEditorPartCollection = new List<EditorPart>();
            BaseSkinnedWebPartSettingsEditorPart skinConfigEditorPart =
                new BaseSkinnedWebPartSettingsEditorPart();

            skinConfigEditorPart.ID = "BaseSkinnedWebPartSettingsEditorPart";
            customEditorPartCollection.Add(skinConfigEditorPart);
            EditorPartCollection editorPartCollection = new EditorPartCollection(customEditorPartCollection);
            return editorPartCollection;
        }

        public override object WebBrowsableObject
        {
            get { return this; }
        }
    }
}
